# Copyright (c) 2003 David Abrahams
# Copyright (c) 2005 Vladimir Prus
# Copyright (c) 2005 Alexey Pakhunov
# Copyright (c) 2006 Bojan Resnik
# Copyright (c) 2006 Ilya Sokolov
# Copyright (c) 2007-2017 Rene Rivera
# Copyright (c) 2008 Jurko Gospodnetic
# Copyright (c) 2014 Microsoft Corporation
# Copyright (c) 2019 Michał Janiszewski
# Copyright (c) 2020 Nikita Kniazev
#
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or copy at
# https://www.bfgroup.xyz/b2/LICENSE.txt)

#| tag::doc[]

[[bbv2.reference.tools.compiler.msvc]]
= Microsoft Visual C++

The `msvc` module supports the
http://msdn.microsoft.com/visualc/[Microsoft Visual C++] command-line
tools on Microsoft Windows. The supported products and versions of
command line tools are listed below:

* Visual Studio 2019-14.2
* Visual Studio 2017—14.1
* Visual Studio 2015—14.0
* Visual Studio 2013—12.0
* Visual Studio 2012—11.0
* Visual Studio 2010—10.0
* Visual Studio 2008—9.0
* Visual Studio 2005—8.0
* Visual Studio .NET 2003—7.1
* Visual Studio .NET—7.0
* Visual Studio 6.0, Service Pack 5--6.5

The user would then call the boost build executable with the toolset set
equal to `msvc-[version number]` for example to build with Visual Studio
2019 one could run:

----
.\b2 toolset=msvc-14.2 target
----

The `msvc` module is initialized using the following syntax:

----
using msvc : [version] : [c++-compile-command] : [compiler options] ;
----

This statement may be repeated several times, if you want to configure
several versions of the compiler.

If the version is not explicitly specified, the most recent version
found in the registry will be used instead. If the special value `all`
is passed as the version, all versions found in the registry will be
configured. If a version is specified, but the command is not, the
compiler binary will be searched in standard installation paths for that
version, followed by PATH.

The compiler command should be specified using forward slashes, and
quoted.

The following options can be provided, using
_`<option-name>option-value syntax`_:

`cflags`::
Specifies additional compiler flags that will be used when compiling C
sources.

`cxxflags`::
Specifies additional compiler flags that will be used when compiling C++
sources.

`compileflags`::
Specifies additional compiler flags that will be used when compiling both C
and C++ sources.

`linkflags`::
Specifies additional command line options that will be passed to the linker.

`assembler`::
The command that compiles assembler sources. If not specified, `ml`
will be used. The command will be invoked after the setup script was
executed and adjusted the PATH variable.

`compiler`::
The command that compiles C and C++ sources. If not specified, `cl`
will be used. The command will be invoked after the setup script was
executed and adjusted the PATH variable.

`compiler-filter`::
Command through which to pipe the output of running the compiler. For
 example to pass the output to STLfilt.

`idl-compiler`::
The command that compiles Microsoft COM interface definition files. If
not specified, `midl` will be used. The command will be invoked after
the setup script was executed and adjusted the PATH variable.

`linker`::
The command that links executables and dynamic libraries. If not
specified, `link` will be used. The command will be invoked after the
setup script was executed and adjusted the PATH variable.

`mc-compiler`::
The command that compiles Microsoft message catalog files. If not
specified, `mc` will be used. The command will be invoked after the
setup script was executed and adjusted the PATH variable.

`resource-compiler`::
The command that compiles resource files. If not specified, `rc` will
be used. The command will be invoked after the setup script was
executed and adjusted the PATH variable.

`setup`::
The filename of the global environment setup script to run before
invoking any of the tools defined in this toolset. Will not be used in
case a target platform specific script has been explicitly specified
for the current target platform. Used setup script will be passed the
target platform identifier (x86, x86_amd64, x86_ia64, amd64 or ia64)
as a parameter. If not specified a default script is chosen based on
the used compiler binary, e.g. `vcvars32.bat` or `vsvars32.bat`.

`setup-amd64`; `setup-i386`; `setup-ia64`::
The filename of the target platform specific environment setup script
to run before invoking any of the tools defined in this toolset. If
not specified the global environment setup script is used.

[[bbv2.reference.tools.compiler.msvc.64]]
== 64-bit support

Starting with version 8.0, Microsoft Visual Studio can generate binaries
for 64-bit processor, both 64-bit flavours of x86 (codenamed
AMD64/EM64T), and Itanium (codenamed IA64). In addition, compilers that
are itself run in 64-bit mode, for better performance, are provided. The
complete list of compiler configurations are as follows (we abbreviate
AMD64/EM64T to just AMD64):

* 32-bit x86 host, 32-bit x86 target
* 32-bit x86 host, 64-bit AMD64 target
* 32-bit x86 host, 64-bit IA64 target
* 64-bit AMD64 host, 64-bit AMD64 target
* 64-bit IA64 host, 64-bit IA64 target

The 32-bit host compilers can be always used, even on 64-bit Windows. On
the contrary, 64-bit host compilers require both 64-bit host processor
and 64-bit Windows, but can be faster. By default, only 32-bit host,
32-bit target compiler is installed, and additional compilers need to be
installed explicitly.

To use 64-bit compilation you should:

1. Configure you compiler as usual. If you provide a path to the
compiler explicitly, provide the path to the 32-bit compiler. If you try
to specify the path to any of 64-bit compilers, configuration will not
work.
2. When compiling, use `address-model=64`, to generate AMD64 code.
3. To generate IA64 code, use `architecture=ia64`

The (AMD64 host, AMD64 target) compiler will be used automatically when
you are generating AMD64 code and are running 64-bit Windows on AMD64.
The (IA64 host, IA64 target) compiler will never be used, since nobody
has an IA64 machine to test.

It is believed that AMD64 and EM64T targets are essentially compatible.
The compiler options `/favor:AMD64` and `/favor:EM64T`, which are
accepted only by AMD64 targeting compilers, cause the generated code to
be tuned to a specific flavor of 64-bit x86. B2 will make use
of those options depending on the value of the`instruction-set` feature.

[[bbv2.reference.tools.compiler.msvc.winrt]]
== Windows Runtime support

Starting with version 11.0, Microsoft Visual Studio can produce binaries
for Windows Store and Phone in addition to traditional Win32 desktop. To
specify which Windows API set to target, use the `windows-api` feature.
Available options are `desktop`, `store`, or `phone`. If not specified,
`desktop` will be used.

When using `store` or `phone` the specified toolset determines what
Windows version is targeted. The following options are available:

* Windows 8.0: toolset=msvc-11.0 windows-api=store
* Windows 8.1: toolset=msvc-12.0 windows-api=store
* Windows Phone 8.0: toolset=msvc-11.0 windows-api=phone
* Windows Phone 8.1: toolset=msvc-12.0 windows-api=phone

For example use the following to build for Windows Store 8.1 with the
ARM architecture:

----
.\b2 toolset=msvc-12.0 windows-api=store architecture=arm
----

Note that when targeting Windows Phone 8.1, version 12.0 didn't include
the vcvars phone setup scripts. They can be separately downloaded from
http://blogs.msdn.com/b/vcblog/archive/2014/07/18/using-boost-libraries-in-windows-store-and-phone-applications.aspx[here].

|# # end::doc[]


################################################################################
#
# MSVC Boost Build toolset module.
# --------------------------------
#
# All toolset versions need to have their location either auto-detected or
# explicitly specified except for the special 'default' version that expects the
# environment to find the needed tools or report an error.
#
################################################################################

import "class" : new ;
import common ;
import feature ;
import generators ;
import mc ;
import midl ;
import os ;
import path ;
import pch ;
import project ;
import property ;
import property-set ;
import rc ;
import sequence ;
import set ;
import testing ;
import toolset ;
import type ;
import virtual-target ;
import version ;


type.register MANIFEST : manifest ;

#| tag::embed-doc[]

[[bbv2.builtin.features.embed-manifest]]`embed-manifest`::
*Allowed values:* `on`, `off`.
+
This feature is specific to the `msvc` toolset (see <<Microsoft Visual C++>>),
and controls whether the manifest files should be embedded inside executables
and shared libraries, or placed alongside them. This feature corresponds to the
IDE option found in the project settings dialog, under Configuration Properties
-> Manifest Tool -> Input and Output -> Embed manifest.

|# # end::embed-doc[]

feature.feature embed-manifest : on off : incidental propagated ;

#| tag::embed-doc[]

[[bbv2.builtin.features.embed-manifest-file]]`embed-manifest-file`::
This feature is specific to the `msvc` toolset (see <<Microsoft Visual C++>>),
and controls which manifest files should be embedded inside executables and
shared libraries. This feature corresponds to the IDE option found in the
project settings dialog, under Configuration Properties -> Manifest Tool ->
Input and Output -> Additional Manifest Files.

|# # end::embed-doc[]

feature.feature embed-manifest-file : : free dependency ;

#| tag::embed-doc[]

[[bbv2.builtin.features.embed-manifest-via]]`embed-manifest-via`::
This feature is specific to the `msvc` toolset (see <<Microsoft Visual C++>>),
and controls whether a manifest should be embedded via linker or manifest tool.

|# # end::embed-doc[]

feature.feature embed-manifest-via : mt linker : incidental propagated ;

type.register PDB : pdb ;


################################################################################
#
# Public rules.
#
################################################################################

# Initialize a specific toolset version configuration. As the result, path to
# compiler and, possible, program names are set up, and will be used when that
# version of compiler is requested. For example, you might have:
#
#    using msvc : 6.5 : cl.exe ;
#    using msvc : 7.0 : Y:/foo/bar/cl.exe ;
#
# The version parameter may be omitted:
#
#    using msvc : : Z:/foo/bar/cl.exe ;
#
# The following keywords have special meanings when specified as versions:
#   - all     - all detected but not yet used versions will be marked as used
#               with their default options.
#   - default - this is an equivalent to an empty version.
#
# Depending on a supplied version, detected configurations and presence 'cl.exe'
# in the path different results may be achieved. The following table describes
# the possible scenarios:
#
#                                      Nothing            "x.y"
# Passed   Nothing       "x.y"         detected,          detected,
# version  detected      detected      cl.exe in path     cl.exe in path
#
# default  Error         Use "x.y"     Create "default"   Use "x.y"
# all      None          Use all       None               Use all
# x.y      -             Use "x.y"     -                  Use "x.y"
# a.b      Error         Error         Create "a.b"       Create "a.b"
#
# "x.y" - refers to a detected version;
# "a.b" - refers to an undetected version.
#
# FIXME: Currently the command parameter and the <compiler> property parameter
# seem to overlap in duties. Remove this duplication. This seems to be related
# to why someone started preparing to replace init with configure rules.
#
rule init (
    # The msvc version being configured. When omitted the tools invoked when no
    # explicit version is given will be configured.
    version ?

    # The command used to invoke the compiler. If not specified:
    #   - if version is given, default location for that version will be
    #     searched
    #
    #   - if version is not given, default locations for MSVC 9.0, 8.0, 7.1, 7.0
    #     and 6.* will be searched
    #
    #   - if compiler is not found in the default locations, PATH will be
    #     searched.
    : command *

    # Options may include:
    #
    #     All options shared by multiple toolset types as handled by the
    #   common.handle-options() rule, e.g. <cflags>, <compileflags>, <cxxflags>,
    #   <fflags> & <linkflags>.
    #
    #   <assembler>
    #   <compiler>
    #   <idl-compiler>
    #   <linker>
    #   <mc-compiler>
    #   <resource-compiler>
    #       Exact tool names to be used by this msvc toolset configuration.
    #
    #   <compiler-filter>
    #       Command through which to pipe the output of running the compiler.
    #     For example to pass the output to STLfilt.
    #
    #   <setup>
    #       Global setup command to invoke before running any of the msvc tools.
    #     It will be passed additional option parameters depending on the actual
    #     target platform.
    #
    #   <setup-amd64>
    #   <setup-i386>
    #   <setup-ia64>
    #   <setup-arm>
    #   <setup-phone-i386>
    #   <setup-phone-arm>
    #       Platform specific setup command to invoke before running any of the
    #     msvc tools used when builing a target for a specific platform, e.g.
    #     when building a 32 or 64 bit executable.
    #
    #   <rewrite-setup-scripts>
    #       Whether to rewrite setup scripts. New scripts will be output in
    #     build tree and will be used instead of originals in build actions.
    #     Possible values:
    #       * on - rewrite scripts, if they do not already exist (default)
    #       * always - always rewrite scripts, even if they already exist
    #       * off - use original setup scripts
    : options *
)
{
    if $(command)
    {
        options += <command>$(command) ;
    }
    configure $(version) : $(options) ;
}


# 'configure' is a newer version of 'init'. The parameter 'command' is passed as
# a part of the 'options' list. See the 'init' rule comment for more detailed
# information.
#
rule configure ( version ? : options * )
{
    switch $(version)
    {
        case "all" :
            if $(options)
            {
                import errors ;
                errors.error "MSVC toolset configuration: options should be"
                    "empty when '$(version)' is specified." ;
            }

            # Configure (i.e. mark as used) all registered versions.
            local all-versions = [ $(.versions).all ] ;
            if ! $(all-versions)
            {
                if $(.debug-configuration)
                {
                    ECHO "notice: [msvc-cfg] Asked to configure all registered"
                        "msvc toolset versions when there are none currently"
                        "registered." ;
                }
            }
            else
            {
                for local v in $(all-versions)
                {
                    # Note that there is no need to skip already configured
                    # versions here as this will request configure-really rule
                    # to configure the version using default options which will
                    # in turn cause it to simply do nothing in case the version
                    # has already been configured.
                    configure-really $(v) ;
                }
            }

        case "default" :
            configure-really : $(options) ;

        case * :
            configure-really $(version) : $(options) ;
    }
}


# Sets up flag definitions dependent on the compiler version used.
# - 'version' is the version of compiler in N.M format.
# - 'conditions' is the property set to be used as flag conditions.
# - 'toolset' is the toolset for which flag settings are to be defined.
#   This makes the rule reusable for other msvc-option-compatible compilers.
#
rule configure-version-specific ( toolset : version : conditions )
{
    toolset.push-checking-for-flags-module unchecked ;
    # Starting with versions 7.0, the msvc compiler have the /Zc:forScope and
    # /Zc:wchar_t options that improve C++ standard conformance, but those
    # options are off by default. If we are sure that the msvc version is at
    # 7.*, add those options explicitly. We can be sure either if user specified
    # version 7.* explicitly or if we auto-detected the version ourselves.
    if ! [ MATCH ^(6\\.) : $(version) ]
    {
        toolset.flags $(toolset).compile OPTIONS $(conditions) : "/Zc:forScope" "/Zc:wchar_t" ;
        toolset.flags $(toolset).compile.c++ C++FLAGS $(conditions) : /wd4675 ;

        # Explicitly disable the 'function is deprecated' warning. Some msvc
        # versions have a bug, causing them to emit the deprecation warning even
        # with /W0.
        toolset.flags $(toolset).compile OPTIONS $(conditions)/<warnings>off : /wd4996 ;

        if [ MATCH "^([78]\\.)" : $(version) ]
        {
            # 64-bit compatibility warning deprecated since 9.0, see
            # http://msdn.microsoft.com/en-us/library/yt4xw8fh.aspx
            toolset.flags $(toolset).compile OPTIONS $(conditions)/<warnings>all : /Wp64 ;
        }
    }

    # 12.0 (VS2013 Update 2) introduced /Zc:inline opt-in standard conformance
    # compiler flag that also similar to linker /opt:ref removes unreferenced
    # variables and functions that have internal linkage
    if ! [ version.version-less [ SPLIT_BY_CHARACTERS [ MATCH "^([0123456789.]+)" : $(version) ] : . ] : 12 ]
    {
        toolset.flags $(toolset).compile OPTIONS $(conditions) : "/Zc:inline" ;

        # /Gy analog for variables: https://devblogs.microsoft.com/cppblog/introducing-gw-compiler-switch/
        toolset.flags $(toolset).compile OPTIONS $(conditions)/<optimization>speed $(conditions)/<optimization>space : /Gw ;
    }

    # 14.0 introduced /Zc:throwingNew opt-in flag that disables a workaround
    # for not throwing operator new in VC up to 6.0
    if ! [ version.version-less [ SPLIT_BY_CHARACTERS [ MATCH "^([0123456789.]+)" : $(version) ] : . ] : 14 ]
    {
        toolset.flags $(toolset).compile C++FLAGS $(conditions) : "/Zc:throwingNew" ;
    }

    # 14.27 (VS2019 Version 16.7) introduced support for ASAN on x86 and x64 CPUs
    # This check however now only tests for 14.2 (which is 16.0) as msvc.jam doesn't distinguish between minor versions (e.g. 14.21..14.28 etc)
    if ! [ version.version-less [ SPLIT_BY_CHARACTERS [ MATCH "^([0123456789.]+)" : $(version) ] : . ] : 14 2 ]
    {
        # General asan compile and link options.
        toolset.flags $(toolset).compile OPTIONS
            $(conditions)/<address-sanitizer>on
            :   /fsanitize=address /FS ;
        toolset.flags $(toolset).link LINKFLAGS
            $(conditions)/<address-sanitizer>on
            :   -incremental\:no ;

        # The various 64 bit runtime asan support libraries and related flags.
        toolset.flags $(toolset).link FINDLIBS_SA
            $(conditions)/<address-sanitizer>on/<address-model>/<runtime-link>shared
            $(conditions)/<address-sanitizer>on/<address-model>64/<runtime-link>shared
            :   clang_rt.asan_dynamic-x86_64
                clang_rt.asan_dynamic_runtime_thunk-x86_64 ;
        toolset.flags $(toolset).link LINKFLAGS
            $(conditions)/<address-sanitizer>on/<address-model>/<runtime-link>shared
            $(conditions)/<address-sanitizer>on/<address-model>64/<runtime-link>shared
            :   /wholearchive\:"clang_rt.asan_dynamic-x86_64.lib"
                /wholearchive\:"clang_rt.asan_dynamic_runtime_thunk-x86_64.lib" ;
        toolset.flags $(toolset).link FINDLIBS_SA
            $(conditions)/<address-sanitizer>on/<address-model>/<runtime-link>static/<main-target-type>EXE
            $(conditions)/<address-sanitizer>on/<address-model>/<runtime-link>static/<main-target-type>UNIT_TEST
            $(conditions)/<address-sanitizer>on/<address-model>64/<runtime-link>static/<main-target-type>EXE
            $(conditions)/<address-sanitizer>on/<address-model>64/<runtime-link>static/<main-target-type>UNIT_TEST
            :   clang_rt.asan-x86_64
                clang_rt.asan_cxx-x86_64 ;
        toolset.flags $(toolset).link LINKFLAGS
            $(conditions)/<address-sanitizer>on/<address-model>/<runtime-link>static/<main-target-type>EXE
            $(conditions)/<address-sanitizer>on/<address-model>/<runtime-link>static/<main-target-type>UNIT_TEST
            $(conditions)/<address-sanitizer>on/<address-model>64/<runtime-link>static/<main-target-type>EXE
            $(conditions)/<address-sanitizer>on/<address-model>64/<runtime-link>static/<main-target-type>UNIT_TEST
            :   /wholearchive\:"clang_rt.asan-x86_64.lib"
                /wholearchive\:"clang_rt.asan_cxx-x86_64.lib" ;
        toolset.flags $(toolset).link.dll FINDLIBS_SA
            $(conditions)/<address-sanitizer>on/<address-model>/<runtime-link>static
            $(conditions)/<address-sanitizer>on/<address-model>64/<runtime-link>static
            :   clang_rt.asan_dll_thunk-x86_64 ;
        toolset.flags $(toolset).link.dll LINKFLAGS
            $(conditions)/<address-sanitizer>on/<address-model>/<runtime-link>static
            $(conditions)/<address-sanitizer>on/<address-model>64/<runtime-link>static
            :   /wholearchive\:"clang_rt.asan_dll_thunk-x86_64.lib" ;

        # The various 32 bit runtime asan support libraries and related flags.
        toolset.flags $(toolset).link FINDLIBS_SA
            $(conditions)/<address-sanitizer>on/<address-model>32/<runtime-link>shared
            :   clang_rt.asan_dynamic-i386 clang_rt.asan_dynamic_runtime_thunk-i386 ;
        toolset.flags $(toolset).link LINKFLAGS
            $(conditions)/<address-sanitizer>on/<address-model>32/<runtime-link>shared
            :   /wholearchive\:"clang_rt.asan_dynamic-i386.lib"
                /wholearchive\:"clang_rt.asan_dynamic_runtime_thunk-i386.lib" ;
        toolset.flags $(toolset).link FINDLIBS_SA
            $(conditions)/<address-sanitizer>on/<address-model>32/<runtime-link>static/<main-target-type>EXE
            $(conditions)/<address-sanitizer>on/<address-model>32/<runtime-link>static/<main-target-type>UNIT_TEST
            :   clang_rt.asan-i386 clang_rt.asan_cxx-i386 ;
        toolset.flags $(toolset).link LINKFLAGS
            $(conditions)/<address-sanitizer>on/<address-model>32/<runtime-link>static/<main-target-type>EXE
            $(conditions)/<address-sanitizer>on/<address-model>32/<runtime-link>static/<main-target-type>UNIT_TEST
            :   /wholearchive\:"clang_rt.asan-i386.lib"
                /wholearchive\:"clang_rt.asan_cxx-i386.lib" ;
        toolset.flags $(toolset).link.dll FINDLIBS_SA
            $(conditions)/<address-sanitizer>on/<address-model>32/<runtime-link>static/<main-target-type>LIB/<link>shared
            :   clang_rt.asan_dll_thunk-i386 ;
        toolset.flags $(toolset).link.dll LINKFLAGS
            $(conditions)/<address-sanitizer>on/<address-model>32/<runtime-link>static/<main-target-type>LIB/<link>shared
            :   /wholearchive\:"clang_rt.asan_dll_thunk-i386.lib" ;
    }

    #
    # Processor-specific optimization.
    #

    if [ MATCH "^([67])" : $(version) ]
    {
        # 8.0 deprecates some of the options.
        toolset.flags $(toolset).compile OPTIONS $(conditions)/<optimization>speed $(conditions)/<optimization>space : /Ogiy /Gs ;
        toolset.flags $(toolset).compile OPTIONS $(conditions)/<optimization>speed : /Ot ;
        toolset.flags $(toolset).compile OPTIONS $(conditions)/<optimization>space : /Os ;

        toolset.flags $(toolset).compile OPTIONS $(conditions)/$(.cpu-arch-i386)/<instruction-set> : /GB ;
        toolset.flags $(toolset).compile OPTIONS $(conditions)/$(.cpu-arch-i386)/<instruction-set>i486 : /G4 ;
        toolset.flags $(toolset).compile OPTIONS $(conditions)/$(.cpu-arch-i386)/<instruction-set>$(.cpu-type-g5) : /G5 ;
        toolset.flags $(toolset).compile OPTIONS $(conditions)/$(.cpu-arch-i386)/<instruction-set>$(.cpu-type-g6) : /G6 ;
        toolset.flags $(toolset).compile OPTIONS $(conditions)/$(.cpu-arch-i386)/<instruction-set>$(.cpu-type-g7) : /G7 ;

        # Improve floating-point accuracy. Otherwise, some of C++ Boost's "math"
        # tests will fail.
        toolset.flags $(toolset).compile OPTIONS $(conditions) : /Op ;

        # 7.1 and below have single-threaded static RTL.
        toolset.flags $(toolset).compile OPTIONS $(conditions)/<runtime-debugging>off/<runtime-link>static/<threading>single : /ML ;
        toolset.flags $(toolset).compile OPTIONS $(conditions)/<runtime-debugging>on/<runtime-link>static/<threading>single : /MLd ;
    }
    else
    {
        # 8.0 and above adds some more options.
        toolset.flags $(toolset).compile OPTIONS $(conditions)/$(.cpu-arch-amd64)/<instruction-set> : "/favor:blend" ;
        toolset.flags $(toolset).compile OPTIONS $(conditions)/$(.cpu-arch-amd64)/<instruction-set>$(.cpu-type-em64t) : "/favor:EM64T" ;
        toolset.flags $(toolset).compile OPTIONS $(conditions)/$(.cpu-arch-amd64)/<instruction-set>$(.cpu-type-amd64) : "/favor:AMD64" ;

        # 8.0 and above only has multi-threaded static RTL.
        toolset.flags $(toolset).compile OPTIONS $(conditions)/<runtime-debugging>off/<runtime-link>static/<threading>single : /MT ;
        toolset.flags $(toolset).compile OPTIONS $(conditions)/<runtime-debugging>on/<runtime-link>static/<threading>single : /MTd ;

        # Specify target machine type so the linker will not need to guess.
        toolset.flags $(toolset).link LINKFLAGS $(conditions)/$(.cpu-arch-amd64) : "/MACHINE:X64" ;
        toolset.flags $(toolset).link LINKFLAGS $(conditions)/$(.cpu-arch-i386)  : "/MACHINE:X86" ;
        toolset.flags $(toolset).link LINKFLAGS $(conditions)/$(.cpu-arch-ia64)  : "/MACHINE:IA64" ;
        toolset.flags $(toolset).link LINKFLAGS $(conditions)/$(.cpu-arch-arm)   : "/MACHINE:ARM" ;
        toolset.flags $(toolset).link LINKFLAGS $(conditions)/$(.cpu-arch-arm64) : "/MACHINE:ARM64" ;

        if [ version.version-less [ SPLIT_BY_CHARACTERS [ MATCH "^([0123456789.]+)" : $(version) ] : . ] : 11 ]
        {
            # Make sure that manifest will be generated even if there is no
            # dependencies to put there.
            toolset.flags $(toolset).link LINKFLAGS $(conditions) : /MANIFEST ;
        }
        else
        {
            toolset.flags $(toolset).link LINKFLAGS $(conditions)/<embed-manifest-via>mt : /MANIFEST ;
            toolset.flags $(toolset).link LINKFLAGS $(conditions)/<embed-manifest-via>linker/<embed-manifest>off : /MANIFEST ;
            toolset.flags $(toolset).link LINKFLAGS $(conditions)/<embed-manifest-via>linker/<embed-manifest>on : "/MANIFEST:EMBED" ;

            local conditionx = [ feature.split $(conditions) ] ;
            toolset.add-defaults $(conditionx:J=,)\:<embed-manifest-via>linker ;
        }
    }

    toolset.pop-checking-for-flags-module ;
}

# Feature for handling targeting different Windows API sets.
feature.feature windows-api : desktop store phone : propagated composite link-incompatible ;
feature.compose <windows-api>store : <define>WINAPI_FAMILY=WINAPI_FAMILY_APP 
    <linkflags>/APPCONTAINER ;
feature.compose <windows-api>phone : <define>WINAPI_FAMILY=WINAPI_FAMILY_PHONE_APP 
    <linkflags>/APPCONTAINER <linkflags>"/NODEFAULTLIB:ole32.lib" <linkflags>"/NODEFAULTLIB:kernel32.lib" <linkflags>WindowsPhoneCore.lib ;
feature.set-default windows-api : desktop ;


# Registers this toolset including all of its flags, features & generators. Does
# nothing on repeated calls.
#
rule register-toolset ( )
{
    if ! msvc in [ feature.values toolset ]
    {
        register-toolset-really ;
    }
}

rule resolve-possible-msvc-version-alias ( version )
{
    if $(.version-alias-$(version))
    {
        version = $(.version-alias-$(version)) ;
    }
    return $(version) ;
}


# Declare action for creating static libraries. If library exists, remove it
# before adding files. See
# http://article.gmane.org/gmane.comp.lib.boost.build/4241 for rationale.
if [ os.name ] in NT
{
    # The 'DEL' command would issue a message to stdout if the file does not
    # exist, so need a check.
    actions archive
    {
        if exist "$(<[1])" DEL "$(<[1])"
        $(.SETUP) $(.LD) $(AROPTIONS) /out:"$(<[1])" @($(<[1]:W).rsp:O=FC:<=@":>=":E="$(>)" $(LIBRARIES_MENTIONED_BY_FILE) "$(LIBRARY_OPTION)$(FINDLIBS_ST).lib" "$(LIBRARY_OPTION)$(FINDLIBS_SA).lib")
    }
}
else
{
    actions archive
    {
        $(.RM) "$(<[1])"
        $(.SETUP) $(.LD) $(AROPTIONS) /out:"$(<[1])" @($(<[1]:W).rsp:O=FC:<=@":>=":E="$(>)" $(LIBRARIES_MENTIONED_BY_FILE) "$(LIBRARY_OPTION)$(FINDLIBS_ST).lib" "$(LIBRARY_OPTION)$(FINDLIBS_SA).lib")
    }
}

rule compile.asm ( targets + : sources * : properties * )
{
    set-setup-command $(targets) : $(properties) ;
}

actions compile.asm
{
    $(.SETUP) $(.ASM) -D$(ASMDEFINES) $(ASMFLAGS) $(USER_ASMFLAGS) $(.ASM_OUTPUT) "$(<:W)" "$(>:W)"
}


rule compile.c ( targets + : sources * : properties * )
{
    set-setup-command $(targets) : $(properties) ;
    get-rspline $(targets) : -TC CFLAGS  ;
    compile-c-c++ $(<) : $(>) [ on $(<) return $(PCH_FILE) ] [ on $(<) return $(PCH_HEADER) ] ;
}


rule compile.c.preprocess ( targets + : sources * : properties * )
{
    set-setup-command $(targets) : $(properties) ;
    get-rspline $(targets) : -TC CFLAGS ;
    preprocess-c-c++ $(<) : $(>) [ on $(<) return $(PCH_FILE) ] [ on $(<) return $(PCH_HEADER) ] ;
}


rule compile.c.pch ( targets + : sources * : properties * )
{
    set-setup-command $(targets) : $(properties) ;
    get-rspline $(targets[1]) : -TC CFLAGS ;
    get-rspline $(targets[2]) : -TC CFLAGS ;
    local pch-source = [ on $(<) return $(PCH_SOURCE) ] ;
    if $(pch-source)
    {
        DEPENDS $(<) : $(pch-source) ;
        compile-c-c++-pch-s $(targets) : $(sources) $(pch-source) ;
    }
    else
    {
        compile-c-c++-pch $(targets) : $(sources) ;
    }
}

toolset.flags msvc YLOPTION : "-Yl" ;

# Action for running the C/C++ compiler without using precompiled headers.
#
# WARNING: Synchronize any changes this in action with intel-win
#
# Notes regarding PDB generation, for when we use
# <debug-symbols>on/<debug-store>database:
#
# 1. PDB_CFLAG is only set for <debug-symbols>on/<debug-store>database, ensuring
#    that the /Fd flag is dropped if PDB_CFLAG is empty.
#
# 2. When compiling executables's source files, PDB_NAME is set on a per-source
#    file basis by rule compile-c-c++. The linker will pull these into the
#    executable's PDB.
#
# 3. When compiling library's source files, PDB_NAME is updated to <libname>.pdb
#    for each source file by rule archive, as in this case compiler must be used
#    to create a single PDB for our library.
#
actions compile-c-c++ bind PDB_NAME PCH_HEADER
{
    $(.SETUP) $(.CC) @($(<[1]:W).rsp:O=FC:<=@":>=":E="$(>[1]:W)" -c -Fo"$(<[1]:W)" $(PDB_CFLAG)"$(PDB_NAME)" -FI"$(PCH_HEADER)" -Yu"$(>[3]:D=)" -Fp"$(>[2]:W)" $(CC_RSPLINE)) $(.CC.FILTER)
}

actions preprocess-c-c++ bind PDB_NAME PCH_HEADER
{
    $(.SETUP) $(.CC) @($(<[1]:W).rsp:O=FC:<=@":>=":E="$(>[1]:W)" -P -Fi"$(<[1]:W)" $(PDB_CFLAG)"$(PDB_NAME)" -FI"$(PCH_HEADER)" -Yu"$(>[3]:D=)" -Fp"$(>[2]:W)" $(CC_RSPLINE))
}

rule compile-c-c++ ( targets + : sources * )
{
    DEPENDS $(<[1]) : [ on $(<[1]) return $(PCH_HEADER) ] ;
    DEPENDS $(<[1]) : [ on $(<[1]) return $(PCH_FILE) ] ;
    PDB_NAME on $(<) = $(<[1]:S=.pdb) ;
    LOCATE on $(<[1]:S=.pdb) = [ on $(<[1]) return $(LOCATE) ] ;
}

rule preprocess-c-c++ ( targets + : sources * )
{
    DEPENDS $(<[1]) : [ on $(<[1]) return $(PCH_HEADER) ] ;
    DEPENDS $(<[1]) : [ on $(<[1]) return $(PCH_FILE) ] ;
    PDB_NAME on $(<) = $(<:S=.pdb) ;
    LOCATE on $(<[1]:S=.pdb) = [ on $(<[1]) return $(LOCATE) ] ;
}

# Action for running the C/C++ compiler using precompiled headers. In addition
# to whatever else it needs to compile, this action also adds a temporary source
# .cpp file used to compile the precompiled headers themselves.
#
# The global .escaped-double-quote variable is used to avoid messing up Emacs
# syntax highlighting in the messy N-quoted code below.
actions compile-c-c++-pch
{
    $(.SETUP) $(.CC) @($(<[1]:W).rsp:O=FC:<=@":>=":E="$(>[2]:W)" -c -Fo"$(<[2]:W)" -Yc"$(>[1]:D=)" $(YLOPTION)"__bjam_pch_symbol_$(>[1]:D=)" -Fp"$(<[1]:W)" $(CC_RSPLINE)) @($(<[1]:W).cpp:<=":>=":E=$(.hash)include $(.escaped-double-quote)$(>[1]:D=)$(.escaped-double-quote)$(.nl)) $(.CC.FILTER)
}


# Action for running the C/C++ compiler using precompiled headers. An already
# built source file for compiling the precompiled headers is expected to be
# given as one of the source parameters.
actions compile-c-c++-pch-s
{
    $(.SETUP) $(.CC) @($(<[1]:W).rsp:O=FC:<=@":>=":E="$(>[2]:W)" -c -Fo"$(<[2]:W)" -Yc"$(>[1]:D=)" $(YLOPTION)"__bjam_pch_symbol_$(>[1]:D=)" -Fp"$(<[1]:W)" $(CC_RSPLINE)) $(.CC.FILTER)
}


rule compile.c++ ( targets + : sources * : properties * )
{
    set-setup-command $(targets) : $(properties) ;
    get-rspline $(targets) : -TP C++FLAGS ;
    compile-c-c++ $(<) : $(>) [ on $(<) return $(PCH_FILE) ] [ on $(<) return $(PCH_HEADER) ] ;
}

rule compile.c++.preprocess ( targets + : sources * : properties * )
{
    set-setup-command $(targets) : $(properties) ;
    get-rspline $(targets) : -TP C++FLAGS ;
    preprocess-c-c++ $(<) : $(>) [ on $(<) return $(PCH_FILE) ] [ on $(<) return $(PCH_HEADER) ] ;
}


rule compile.c++.pch ( targets + : sources * : properties * )
{
    set-setup-command $(targets) : $(properties) ;
    get-rspline $(targets[1]) : -TP C++FLAGS ;
    get-rspline $(targets[2]) : -TP C++FLAGS ;
    local pch-source = [ on $(<) return $(PCH_SOURCE) ] ;
    if $(pch-source)
    {
        DEPENDS $(<) : $(pch-source) ;
        compile-c-c++-pch-s $(targets) : $(sources) $(pch-source) ;
    }
    else
    {
        compile-c-c++-pch $(targets) : $(sources) ;
    }
}

rule compile.idl ( targets + : sources * : properties * )
{
    set-setup-command $(targets) : $(properties) ;
}

# See midl.jam for details.
#
actions compile.idl
{
    $(.SETUP) $(.IDL)  @($(<[1]:W).rsp:O=FC:<=@":>=":E="$(>:W)" -D$(DEFINES) "-I$(INCLUDES:W)" -U$(UNDEFS) $(MIDLFLAGS) /tlb "$(<[1]:W)" /h "$(<[2]:W)" /iid "$(<[3]:W)" /proxy "$(<[4]:W)" /dlldata "$(<[5]:W)")
    $(.TOUCH_FILE) "$(<[4]:W)"
    $(.TOUCH_FILE) "$(<[5]:W)"
}

rule compile.mc ( targets + : sources * : properties * )
{
    set-setup-command $(targets) : $(properties) ;
}

actions compile.mc
{
    $(.SETUP) $(.MC) $(MCFLAGS) -h "$(<[1]:DW)" -r "$(<[2]:DW)" "$(>:W)"
}


rule compile.rc ( targets + : sources * : properties * )
{
    set-setup-command $(targets) : $(properties) ;
}

actions compile.rc
{
    $(.SETUP) $(.RC)  -l 0x409 -U$(UNDEFS) -D$(DEFINES) -I"$(INCLUDES:W)" -fo "$(<:W)" "$(>:W)"
}

toolset.uses-features msvc.link : <embed-manifest> <embed-manifest-file> ;

rule link ( targets + : sources * : properties * )
{
    set-setup-command $(targets) : $(properties) ;
    if <embed-manifest>on in $(properties) && <embed-manifest-via>mt in $(properties)
    {
        if [ feature.get-values <embed-manifest-file> : $(properties) ]
        {
            DEPENDS $(<) : [ on $(<) return $(EMBED_MANIFEST_FILE) ] ;
            msvc.manifest.user $(targets) $(EMBED_MANIFEST_FILE) : $(sources) : $(properties) ;
        }
        else
        {
            msvc.manifest $(targets) : $(sources) : $(properties) ;
        }
    }
}

rule link.dll ( targets + : sources * : properties * )
{
    set-setup-command $(targets) : $(properties) ;
    DEPENDS $(<) : [ on $(<) return $(DEF_FILE) ] ;
    local import-lib ;
    if ! <suppress-import-lib>true in $(properties)
    {
        import-lib = $(targets[2]) ;
        IMPORT_LIB on $(targets) = $(import-lib) ;
    }
    # On msvc-14.1, the linker might not touch the import library
    # if the exports do not change.  (Apparently this could also
    # happen for incremental linking, which is why we disable it,
    # but that no longer seems to be enough).
    # Therefore, don't update the import library just because
    # it's out-dated.  It will be force updated, when the dll
    # is updated.  Also, make it so that anything that depends
    # on it depends on the dll as well.
    NOUPDATE $(import-lib) ;
    INCLUDES $(import-lib) : $(targets[1]) ;
    if <embed-manifest>on in $(properties) && <embed-manifest-via>mt in $(properties)
    {
        if [ feature.get-values <embed-manifest-file> : $(properties) ]
        {
            DEPENDS $(<) : [ on $(<) return $(EMBED_MANIFEST_FILE) ] ;
            msvc.manifest.dll.user $(targets) $(EMBED_MANIFEST_FILE) : $(sources) : $(properties) ;
        }
        else
        {
            msvc.manifest.dll $(targets) : $(sources) : $(properties) ;
        }
    }
}

# Incremental linking a DLL causes no end of problems: if the actual exports do
# not change, the import .lib file is never updated. Therefore, the .lib is
# always out-of-date and gets rebuilt every time. I am not sure that incremental
# linking is such a great idea in general, but in this case I am sure we do not
# want it.

# Windows manifest is a new way to specify dependencies on managed DotNet
# assemblies and Windows native DLLs. The manifests are embedded as resources
# and are useful in any PE target (both DLL and EXE).

{
    actions link bind DEF_FILE LIBRARIES_MENTIONED_BY_FILE MANIFEST_FILE
    {
        $(.SETUP) $(.LD) @($(<[1]:W).rsp:O=FC:<=@":>=":E="$(>)" $(LIBRARIES_MENTIONED_BY_FILE) $(LIBRARIES) "$(LIBRARY_OPTION)$(FINDLIBS_ST).lib" "$(LIBRARY_OPTION)$(FINDLIBS_SA).lib") $(LINKOPT) $(LINKFLAGS) /out:"$(<[1]:W)" /LIBPATH:"$(LINKPATH:W)" /MANIFESTINPUT:"$(MANIFEST_FILE)"
    }

    actions manifest
    {
        $(.SETUP) $(.MT) -manifest "$(<[1]).manifest" "-outputresource:$(<[1]);1"
    }

    actions manifest.user bind EMBED_MANIFEST_FILE
    {
        $(.SETUP) $(.MT) -manifest "$(EMBED_MANIFEST_FILE)" "-outputresource:$(<[1]);1"
    }

    actions link.dll bind IMPORT_LIB DEF_FILE LIBRARIES_MENTIONED_BY_FILE MANIFEST_FILE
    {
        $(.SETUP) $(.LD) @($(<[1]:W).rsp:O=FC:<=@":>=":E="$(>)" $(LIBRARIES_MENTIONED_BY_FILE) $(LIBRARIES) "$(LIBRARY_OPTION)$(FINDLIBS_ST).lib" "$(LIBRARY_OPTION)$(FINDLIBS_SA).lib") $(LINKOPT) $(LINKFLAGS) /out:"$(<[1]:W)" /LIBPATH:"$(LINKPATH:W)" /MANIFESTINPUT:"$(MANIFEST_FILE)" /DLL /IMPLIB:"$(IMPORT_LIB:W)" /def:"$(DEF_FILE)"
    }

    actions manifest.dll
    {
        $(.SETUP) $(.MT) -manifest "$(<[1]).manifest" "-outputresource:$(<[1]);2"
    }

    actions manifest.dll.user bind EMBED_MANIFEST_FILE
    {
        $(.SETUP) $(.MT) -manifest "$(EMBED_MANIFEST_FILE)" "-outputresource:$(<[1]);2"
    }
}

# This rule sets up the pdb file that will be used when generating static
# libraries and the debug-store option is database, so that the compiler puts
# all the debug info into a single .pdb file named after the library.
#
# Poking at source targets this way is probably not clean, but it is the
# easiest approach.
#
rule archive ( targets + : sources * : properties * )
{
    set-setup-command $(targets) : $(properties) ;
    PDB_NAME on $(>) = $(<[1]:S=.pdb) ;
    LOCATE on $(<[1]:S=.pdb) = [ on $(<[1]) return $(LOCATE) ] ;
}


################################################################################
#
# Classes.
#
################################################################################

class msvc-pch-generator : pch-generator
{
    import property-set ;

    rule run-pch ( project name ? : property-set : sources * )
    {
        # Searching for the header and source file in the sources.
        local pch-header ;
        local pch-source ;
        for local s in $(sources)
        {
            if [ type.is-derived [ $(s).type ] H ]
            {
                pch-header = $(s) ;
            }
            else if
                [ type.is-derived [ $(s).type ] CPP ] ||
                [ type.is-derived [ $(s).type ] C ]
            {
                pch-source = $(s) ;
            }
        }

        if ! $(pch-header)
        {
            import errors : user-error : errors.user-error ;
            errors.user-error "can not build pch without pch-header" ;
        }

        # If we do not have the PCH source - that is fine. We will just create a
        # temporary .cpp file in the action.
        local pch-header-dir = [ $(pch-header).path ] ;
        local generated = [ generator.run $(project) $(name)
            : [ property-set.create
                # Passing of <pch-source> is a dirty trick, needed because
                # non-composing generators with multiple inputs are subtly
                # broken. For more detailed information see:
                # https://zigzag.cs.msu.su:7813/boost.build/ticket/111
                <pch-source>$(pch-source) "<include>$(pch-header-dir)"
                [ $(property-set).raw ] ]
            : $(pch-header) ] ;

        local pch-file ;
        for local g in $(generated[2-])
        {
            if [ type.is-derived [ $(g).type ] PCH ]
            {
                pch-file = $(g) ;
            }
        }

        return [ $(generated[1]).add-raw <pch-header>$(pch-header)
            <pch-file>$(pch-file) ] $(generated[2-]) ;
    }
}


################################################################################
#
# Local rules.
#
################################################################################

# Detects versions listed as '.known-versions' by checking registry information,
# environment variables & default paths. Supports both native Windows and
# Cygwin.
#
local rule auto-detect-toolset-versions ( )
{
    if [ os.name ] in NT CYGWIN
    {
        # Get installation paths from the registry.
        for local i in $(.known-versions)
        {
            if $(.version-$(i)-reg)
            {
                local vc-path ;
                for local x in "" "Wow6432Node\\"
                {
                    vc-path += [ W32_GETREG
                        "HKEY_LOCAL_MACHINE\\SOFTWARE\\"$(x)"\\Microsoft\\"$(.version-$(i)-reg)
                        : "ProductDir" ] ;
                }

                if $(vc-path)
                {
                    vc-path = [ path.join [ path.make-NT $(vc-path[1]) ] "bin" ] ;
                    register-configuration $(i) : [ path.native $(vc-path[1]) ] ;
                }
            }
        }
    }

    # Check environment and default installation paths.
    for local i in $(.known-versions)
    {
        if ! $(i) in [ $(.versions).all ]
        {
            register-configuration $(i) : [ default-path $(i) ] ;
        }
    }
}

actions write-setup-script
{
    @($(STDOUT):E=$(FILE_CONTENTS:J=$(.nl))) > "$(<)"
}

if [ os.name ] = NT
{
    local rule call-batch-script ( command )
    {
        return "call $(command) >nul$(.nl)" ;
    }
}
else
{
    # On cygwin, we need to run both the batch script
    # and the following command in the same instance
    # of cmd.exe.
    local rule call-batch-script ( command )
    {
        return "cmd.exe /S /C call $(command) \">nul\" \"&&\" " ;
    }
}

# Local helper rule to create the vcvars setup command for given architecture
# and options.
#
local rule generate-setup-cmd ( version : command : parent : options * : cpu : global-setup ? : default-global-setup-options : default-setup )
{
    local setup-options ;
    local setup = [ feature.get-values <setup-$(cpu)> : $(options) ] ;

    if ! $(setup)-is-defined
    {
        if $(global-setup)-is-defined
        {
            setup = $(global-setup) ;

            # If needed we can easily add using configuration flags
            # here for overriding which options get passed to the
            # global setup command for which target platform:
            # setup-options = [ feature.get-values <setup-options-$(c)> : $(options) ] ;
            setup-options ?= $(default-global-setup-options) ;
        }
        else
        {
            if [ MATCH "(14.3)" : $(version) ]
            {
                if $(.debug-configuration)
                {
                    ECHO "notice: [generate-setup-cmd] $(version) is 14.3" ;
                }
                parent = [ path.native [ path.join  $(parent) "..\\..\\..\\..\\..\\Auxiliary\\Build" ] ] ;
            }
            else if [ MATCH "(14.2)" : $(version) ]
            {
                if $(.debug-configuration)
                {
                    ECHO "notice: [generate-setup-cmd] $(version) is 14.2" ;
                }
                parent = [ path.native [ path.join  $(parent) "..\\..\\..\\..\\..\\Auxiliary\\Build" ] ] ;
            }
            else if [ MATCH "(14.1)" : $(version) ]
            {
                if $(.debug-configuration)
                {
                    ECHO "notice: [generate-setup-cmd] $(version) is 14.1" ;
                }
                parent = [ path.native [ path.join  $(parent) "..\\..\\..\\..\\..\\Auxiliary\\Build" ] ] ;
            }
            setup = [ locate-default-setup $(command) : $(parent) : $(default-setup) ] ;
            setup ?= [ path.join  $(parent) "vcvarsall.bat" ] ;
        }
    }

    return $(setup) "$(setup-options:J= )" ;
}

# Worker for set-setup-command.  Usable in a virtual-target.action.
rule adjust-setup-command ( new-setup : setup : properties * )
{
    local internal = $(new-setup:S=.read) ;
    NOTFILE $(internal) ;
    local setup-options = [ property.select <msvc.setup-options> : $(properties) ] ;
    setup-options = $(setup-options:G=:E=) ;
    DEPENDS $(internal) : $(setup) ;
    DEPENDS $(new-setup) : $(internal) ;
    REBUILDS $(new-setup) : $(internal) ;
    msvc.read-setup $(internal) : $(setup) ;
    msvc.write-setup-script $(new-setup) : $(setup) ;
    __ACTION_RULE__ on $(internal) = msvc.rewrite-setup $(setup) $(setup-options) $(new-setup) ;
}

# This doesn't actually do anything.  It's merely
# used as a trigger for __ACTION_RULE__.
actions quietly read-setup { }

# Calculates the changes to the environment make by setup-script
# Should be used as a callback for __ACTION_RULE__
local rule rewrite-setup ( setup-script setup-options new-setup : target : * )
{
    local setup-path = [ on $(setup-script) return $(LOCATE) $(SEARCH) ] ;
    setup-path = $(setup-path[1]) ;
    local command = "\"$(setup-script:G=:R=$(setup-path))\" $(setup-options)" ;
    local original-vars = [ SPLIT_BY_CHARACTERS [ SHELL set ] : "\n" ] ;
    local new-vars = [ SPLIT_BY_CHARACTERS [ SHELL "$(command) >nul && set" ] : "\n" ] ;
    local diff-vars = [ set.difference $(new-vars) : $(original-vars) ] ;
    if $(diff-vars)
    {
        FILE_CONTENTS on $(new-setup) = "REM $(command)" "SET "$(diff-vars) ;
    }
}

IMPORT msvc : rewrite-setup : : msvc.rewrite-setup ;

# Helper rule to generate a faster alternative to MSVC setup scripts.
# We used to call MSVC setup scripts directly in every action, however in
# newer MSVC versions (10.0+) they make long-lasting registry queries
# which have a significant impact on build time.
local rule set-setup-command ( targets * : properties * )
{
    if ! [ on $(targets) return $(.SETUP) ]
    {
        local setup-script = [ on $(targets) return $(.SETUP-SCRIPT) ] ;
        # If no setup script was given, then we don't need to do anything.
        if ! $(setup-script)
        {
            return ;
        }
        local setup-options = [ on $(targets) return $(.SETUP-OPTIONS) ] ;
        local key = .setup-command-$(setup-script:E=)-$(setup-options:E=) ;
        if ! $($(key))
        {
            properties = [ feature.expand $(properties) ] ;
            properties = [ property.select <toolset> <toolset-msvc:version> <architecture> <address-model> <windows-api> <relevant> : $(properties) ] ;
            local ps = [ property-set.create $(properties) <msvc.setup-options>$(setup-options) ] ;
            local original = [ virtual-target.from-file $(setup-script) : [ path.pwd ] : $(.project) ] ;
            local action = [ new non-scanning-action $(original) : msvc.adjust-setup-command : $(ps) ] ;
            local new-setup = [ virtual-target.register [ new file-target msvc-setup.bat exact : : $(.project) : $(action) ] ] ;
            local command = [ $(new-setup).actualize ] ;
            local path = [ on $(command) return $(LOCATE) ] ;
            local block-update = $(command:S=.nup) ;
            NOUPDATE $(block-update) ;
            NOTFILE $(block-update) ;
            DEPENDS $(block-update) : $(command) ;
            if [ on $(targets) return $(.REWRITE-SETUP) ]
            {
                ALWAYS $(command) ;
            }
            $(key) = [ call-batch-script "\"$(command:WG=:R=$(path))\" $(setup-options:E=)" ] $(block-update) ;
        }
        DEPENDS $(targets) : $($(key)[2]) ;
        .SETUP on $(targets) = $($(key)[1]) ;
    }
}

# Worker rule for toolset version configuration. Takes an explicit version id or
# nothing in case it should configure the default toolset version (the first
# registered one or a new 'default' one in case no toolset versions have been
# registered yet).
#
local rule configure-really ( version ? : options * )
{
    local command = [ feature.get-values <command> : $(options) ] ;

    if ! $(version) && ! $(command)
    {
        # We were given neither a command, nor a version.
        # Take the best registered (i.e. auto-detected) version.
        # FIXME: consider whether an explicitly specified setup script
        # should disable this logic.  We already won't get here if
        # there is a user specified command.
        version = [ $(.versions).all ] ;
        for local known in $(.known-versions)
        {
            if $(known) in $(version)
            {
                version = $(known) ;
                break ;
            }
        }
        # version might still have multiple elements if no versions
        # were auto-detected, but an unknown version was configured
        # manually.
        version = $(version[1]) ;
    }

    # Handle a user-provided command, and deduce the version if necessary.
    # If the user-requested version was not autodetected and no command
    # was given, attempt to find it in PATH
    if $(command) || ! ( $(version:E=default) in [ $(.versions).all ] )
    {
        local found-command = [ common.get-invocation-command-nodefault msvc : cl.exe : $(command) ] ;

        if $(found-command)
        {
            command = $(found-command) ;
            if ! $(command:D)
            {
                local path = [ common.get-absolute-tool-path $(command) ] ;
                command = $(command:R=$(path)) ;
            }
        }
        else
        {
            # If we still failed to find cl.exe, bail out.
            ECHO ;
            ECHO warning\:
                "Did not find command for MSVC toolset."
                "If you have Visual Studio 2017 installed you will need to"
                "specify the full path to the command,"
                "set VS150COMNTOOLS for your installation,"
                "or"
                "build from the 'Visual Studio Command Prompt for VS 2017'."
                ;
            ECHO ;
            command ?= cl.exe ;
        }

        if ! $(version)
        {
            # Even if version is not explicitly specified, try to detect the
            # version from the path.
            # FIXME: We currently detect both Microsoft Visual Studio 9.0 and
            # 9.0express as 9.0 here.
            if [ MATCH "(MSVC\\\\14.3)" : $(command) ]
            {
                version = 14.3 ;
            }
            else if [ MATCH "(MSVC\\\\14.2)" : $(command) ]
            {
                version = 14.2 ;
            }
            else if [ MATCH "(MSVC\\\\14.1)" : $(command) ]
            {
                version = 14.1 ;
            }
            else if [ MATCH "(Microsoft Visual Studio 14)" : $(command) ]
            {
                version = 14.0 ;
            }
            else if [ MATCH "(Microsoft Visual Studio 12)" : $(command) ]
            {
                version = 12.0 ;
            }
            else if [ MATCH "(Microsoft Visual Studio 11)" : $(command) ]
            {
                version = 11.0 ;
            }
            else if [ MATCH "(Microsoft Visual Studio 10)" : $(command) ]
            {
                version = 10.0 ;
            }
            else if [ MATCH "(Microsoft Visual Studio 9)" : $(command) ]
            {
                version = 9.0 ;
            }
            else if [ MATCH "(Microsoft Visual Studio 8)" : $(command) ]
            {
                version = 8.0 ;
            }
            else if [ MATCH "(NET 2003[\/\\]VC7)" : $(command) ]
            {
                version = 7.1 ;
            }
            else if [ MATCH "(Microsoft Visual C\\+\\+ Toolkit 2003)" :
                $(command) ]
            {
                version = 7.1toolkit ;
            }
            else if [ MATCH "(.NET[\/\\]VC7)" : $(command) ]
            {
                version = 7.0 ;
            }
            else
            {
                version = 6.0 ;
            }
        }
    }

    # Version alias -> real version number.
    version = [ resolve-possible-msvc-version-alias $(version) ] ;

    # Check whether the selected configuration is already in use.
    if $(version) in [ $(.versions).used ]
    {
        # Allow multiple 'toolset.using' calls for the same configuration if the
        # identical sets of options are used.
        if $(options) && ( $(options) != [ $(.versions).get $(version) : options ] )
        {
            import errors ;
            errors.user-error "MSVC toolset configuration: Toolset version"
                "'$(version)' already configured." ;
        }
    }
    else
    {
        # Register a new configuration.
        $(.versions).register $(version) ;
        $(.versions).set $(version) : options : $(options) ;

        # Mark the configuration as 'used'.
        $(.versions).use $(version) ;

        # Generate conditions and save them.
        local conditions = [ common.check-init-parameters msvc : version $(version) ] ;

        $(.versions).set $(version) : conditions : $(conditions) ;

        command ?= [ $(.versions).get $(version) : default-command ] ;

        # For 14.1+ we need the exact version as MS is planning rolling updates
        # that will cause our `setup-cmd` to become invalid
        exact-version = [ MATCH "(14\.[1-9][0-9]\.[0-9\.]+)" : $(command) ] ;

        common.handle-options msvc : $(conditions) : $(command) : $(options) ;

        # Generate and register setup command.

        local below-8.0 = [ MATCH "^([67]\\.)" : $(version) ] ;
        local below-11.0 = [ MATCH "^([6789]\\.|10\\.)" : $(version) ] ;

        local cpu = i386 amd64 ia64 arm arm64 ;
        if $(below-8.0)
        {
            cpu = i386 ;
        }
        else if $(below-11.0)
        {
            cpu = i386 amd64 ia64 ;
        }

        local setup-amd64 ;
        local setup-i386 ;
        local setup-ia64 ;
        local setup-arm ;
        local setup-arm64 ;
        local setup-phone-i386 ;
        local setup-phone-arm ;

        if $(command)
        {
            # TODO: Note that if we specify a non-existant toolset version then
            # this rule may find and use a corresponding compiler executable
            # belonging to an incorrect toolset version. For example, if you
            # have only MSVC 7.1 installed, have its executable on the path and
            # specify you want Boost Build to use MSVC 9.0, then you want Boost
            # Build to report an error but this may cause it to silently use the
            # MSVC 7.1 compiler even though it thinks it is using the msvc-9.0
            # toolset version.
            command = [ common.get-absolute-tool-path $(command[-1]) ] ;
        }

        if $(command)
        {
            local parent = [ path.make $(command) ] ;
            parent = [ path.parent $(parent) ] ;
            parent = [ path.native $(parent) ] ;

            # Setup will be used if the command name has been specified. If
            # setup is not specified explicitly then a default setup script will
            # be used instead. Setup scripts may be global or architecture/
            # /platform/cpu specific. Setup options are used only in case of
            # global setup scripts.

            # Default setup scripts provided with different VC distributions:
            #
            #   VC 7.1 had only the vcvars32.bat script specific to 32 bit i386
            # builds. It was located in the bin folder for the regular version
            # and in the root folder for the free VC 7.1 tools.
            #
            #   Later 8.0 & 9.0 versions introduce separate platform specific
            # vcvars*.bat scripts (e.g. 32 bit, 64 bit AMD or 64 bit Itanium)
            # located in or under the bin folder. Most also include a global
            # vcvarsall.bat helper script located in the root folder which runs
            # one of the aforementioned vcvars*.bat scripts based on the options
            # passed to it. So far only the version coming with some PlatformSDK
            # distributions does not include this top level script but to
            # support those we need to fall back to using the worker scripts
            # directly in case the top level script can not be found.

            local global-setup = [ feature.get-values <setup> : $(options) ] ;
            global-setup = $(global-setup[1]) ;
            local global-setup-phone = $(global-setup) ;
            if ! $(below-8.0)
            {
                global-setup ?= [ locate-default-setup $(command) : $(parent) :
                    vcvarsall.bat ] ;
            }

            local default-setup-amd64 = vcvars64.bat ;
            local default-setup-i386  = vcvars32.bat ;
            local default-setup-ia64  = vcvarsx86_ia64.bat ;
            local default-setup-arm   = vcvarsx86_arm.bat ;
            local default-setup-arm64 = vcvarsx86_arm64.bat ;
            local default-setup-phone-i386 = vcvarsphonex86.bat ;
            local default-setup-phone-arm = vcvarsphonex86_arm.bat ;

            # http://msdn2.microsoft.com/en-us/library/x4d2c09s(VS.80).aspx and
            # http://msdn2.microsoft.com/en-us/library/x4d2c09s(vs.90).aspx
            # mention an x86_IPF option, that seems to be a documentation bug
            # and x86_ia64 is the correct option.
            local default-global-setup-options-amd64 = x86_amd64 ;
            local default-global-setup-options-i386  = x86 ;
            local default-global-setup-options-ia64  = x86_ia64 ;
            local default-global-setup-options-arm   = x86_arm ;
            local default-global-setup-options-arm64 = x86_arm64 ;

            # When using 64-bit Windows, and targeting 64-bit, it is possible to
            # use a native 64-bit compiler, selected by the "amd64" & "ia64"
            # parameters to vcvarsall.bat. There are two variables we can use --
            # PROCESSOR_ARCHITECTURE and PROCESSOR_IDENTIFIER. The first is
            # 'x86' when running 32-bit Windows, no matter which processor is
            # used, and 'AMD64' on 64-bit windows on x86 (either AMD64 or EM64T)
            # Windows.
            #
            if [ MATCH ^(AMD64) : [ os.environ PROCESSOR_ARCHITECTURE ] ]
            {
                default-global-setup-options-amd64 = amd64 ;
            }
            # When B2 itself is running as a 32-bit process on 64-bit
            # Windows, the above test will fail (since WOW64 simulates a 32-bit
            # environment, including environment values).  So check the WOW64
            # variable PROCESSOR_ARCHITEW6432 as well.
            if [ MATCH ^(AMD64) : [ os.environ PROCESSOR_ARCHITEW6432 ] ]
            {
                default-global-setup-options-amd64 = amd64 ;
            }
            # TODO: The same 'native compiler usage' should be implemented for
            # the Itanium platform by using the "ia64" parameter. For this
            # though we need someone with access to this platform who can find
            # out how to correctly detect this case.
            else if $(somehow-detect-the-itanium-platform)
            {
                default-global-setup-options-ia64 = ia64 ;
            }

            for local c in $(cpu)
            {
                exact-version ?= $(version) ;
                setup-$(c) = [ generate-setup-cmd $(exact-version) : $(command) : $(parent) : $(options) : $(c) : $(global-setup) : $(default-global-setup-options-$(c)) : $(default-setup-$(c)) ] ;
            }

            # Windows phone has different setup scripts, located in a different directory hierarchy.
            # The 11.0 toolset can target Windows Phone 8.0 and the 12.0 toolset can target Windows Phone 8.1,
            # each of which have a different directory for their vcvars setup scripts.
            local phone-parent = [ path.native [ path.join $(parent) WPSDK ] ] ;
            local phone-directory = $(phone-parent) ;
            if [ MATCH "(11.0)" : $(version) ]
            {
                phone-directory = [ path.native [ path.join $(phone-directory) WP80 ] ] ;
            }
            else if [ MATCH "(12.0)" : $(version) ]
            {
                phone-directory = [ path.native [ path.join $(phone-directory) WP81 ] ] ;
            }
            global-setup-phone ?= [ locate-default-setup $(phone-directory) : $(phone-parent) : vcvarsphoneall.bat ] ;

            # If can't locate default phone setup script then this VS version doesn't support Windows Phone.
            if $(global-setup-phone)-is-defined
            {
                # i386 CPU is for the Windows Phone emulator in Visual Studio.
                local phone-cpu = i386 arm ;
                for local c in $(phone-cpu)
                {
                    setup-phone-$(c) = [ generate-setup-cmd $(version) : $(phone-directory) : $(phone-parent) : $(options) : $(c) : $(global-setup-phone) : $(default-global-setup-options-$(c)) : $(default-setup-phone-$(c)) ] ;
                }
            }
        }

        # Get tool names (if any) and finish setup.

        compiler = [ feature.get-values <compiler> : $(options) ] ;
        compiler ?= cl ;

        linker = [ feature.get-values <linker> : $(options) ] ;
        linker ?= link ;

        resource-compiler = [ feature.get-values <resource-compiler> : $(options) ] ;
        resource-compiler ?= rc ;

        # Turn on some options for i386 assembler
        #  -coff  generate COFF format object file (compatible with cl.exe output)
        local default-assembler-amd64 = ml64 ;
        local default-assembler-i386  = "ml -coff" ;
        local default-assembler-ia64  = ias ;
        local default-assembler-arm   = armasm ;
        local default-assembler-arm64 = armasm64 ;

        # For the assembler the following options are turned on by default:
        #
        #   -Zp4   align structures to 4 bytes
        #   -Cp    preserve case of user identifiers
        #   -Cx    preserve case in publics, externs
        #
        local assembler-flags-amd64 = "-c -Zp4 -Cp -Cx" ;
        local assembler-flags-i386  = "-c -Zp4 -Cp -Cx" ;
        local assembler-flags-ia64  = "-c -Zp4 -Cp -Cx" ;
        local assembler-flags-arm   = "" ;
        local assembler-flags-arm64 = "" ;

        local assembler-output-flag-amd64 = -Fo ;
        local assembler-output-flag-i386  = -Fo ;
        local assembler-output-flag-ia64  = -Fo ;
        local assembler-output-flag-arm   = -o ;
        local assembler-output-flag-arm64 = -o ;

        assembler = [ feature.get-values <assembler> : $(options) ] ;

        idl-compiler = [ feature.get-values <idl-compiler> : $(options) ] ;
        idl-compiler ?= midl ;

        mc-compiler = [ feature.get-values <mc-compiler> : $(options) ] ;
        mc-compiler ?= mc ;

        manifest-tool = [ feature.get-values <manifest-tool> : $(options) ] ;
        manifest-tool ?= mt ;

        local cc-filter = [ feature.get-values <compiler-filter> : $(options) ]
            ;

        for local c in $(cpu)
        {
            # Setup script is not required in some configurations.
            setup-$(c) ?= "" ;

            local cpu-conditions = $(conditions)/$(.cpu-arch-$(c)) ;

            if $(.debug-configuration)
            {
                for local cpu-condition in $(cpu-conditions)
                {
                    ECHO "notice: [msvc-cfg] condition: '$(cpu-condition)', setup: '$(setup-$(c):J= )'" ;
                }
            }

            local cpu-assembler = $(assembler) ;
            cpu-assembler ?= $(default-assembler-$(c)) ;
            local assembler-flags = $(assembler-flags-$(c)) ;
            local assembler-output-flag = $(assembler-output-flag-$(c)) ;

            for local api in desktop store phone
            {
                local setup-script = $(setup-$(c)) ;
                if $(api) = phone
                {
                    setup-script = $(setup-phone-$(c)) ;
                }

                if <rewrite-setup-scripts>always in $(options)
                {
                    toolset.flags msvc .REWRITE-SETUP <windows-api>$(api)/$(cpu-conditions) : true ;
                }

                if ! $(setup-script)
                {
                    # Should we try to set up some error handling or fallbacks here?
                }
                else if <rewrite-setup-scripts>off in $(options) || [ os.name ] != NT
                {
                    toolset.flags msvc .SETUP <windows-api>$(api)/$(cpu-conditions) : [ call-batch-script "\"$(setup-script[1]:W)\" $(setup-script[2-]:E=)" ] ;
                }
                else
                {
                    toolset.flags msvc .SETUP-SCRIPT <windows-api>$(api)/$(cpu-conditions) : $(setup-script[1]) ;
                    toolset.flags msvc .SETUP-OPTIONS <windows-api>$(api)/$(cpu-conditions) : $(setup-script[2-]) ;
                }

                toolset.flags msvc.compile .RC  <windows-api>$(api)/$(cpu-conditions) : $(resource-compiler) ;
                toolset.flags msvc.compile .IDL <windows-api>$(api)/$(cpu-conditions) : $(idl-compiler) ;
                toolset.flags msvc.compile .MC  <windows-api>$(api)/$(cpu-conditions) : $(mc-compiler) ;
                toolset.flags msvc.link    .MT  <windows-api>$(api)/$(cpu-conditions) : $(manifest-tool)  ;

                if $(api) = desktop
                {
                    toolset.flags msvc.compile .CC  <windows-api>$(api)/$(cpu-conditions) : $(compiler)   ;
                }
                else
                {
                    toolset.flags msvc.compile .CC  <windows-api>$(api)/$(cpu-conditions) : $(compiler)  ;
                }
                toolset.flags msvc.compile .ASM <windows-api>$(api)/$(cpu-conditions) : $(cpu-assembler) $(assembler-flags)  ;
                toolset.flags msvc.compile .ASM_OUTPUT <windows-api>$(api)/$(cpu-conditions) : $(assembler-output-flag) ;
                toolset.flags msvc.link    .LD  <windows-api>$(api)/$(cpu-conditions) : $(linker) /NOLOGO "/INCREMENTAL:NO" ;
                toolset.flags msvc.archive .LD  <windows-api>$(api)/$(cpu-conditions) : $(linker) /lib /NOLOGO  ;
            }

            if $(cc-filter)
            {
                toolset.flags msvc .CC.FILTER $(cpu-conditions) : "|" $(cc-filter) ;
            }
        }

        # Starting with Visual Studio 2013 the CRT is split into a desktop and app dll.
        # If targeting WinRT and 12.0 set lib path to link against app CRT.
        if [ MATCH "(12)" : $(version) ]
        {
            local storeLibPath = [ path.join $(parent) "lib/store" ] ;
            toolset.flags msvc.link LINKPATH $(conditions)/<windows-api>store/$(.cpu-arch-i386) : [ path.native $(storeLibPath) ] ;
            toolset.flags msvc.link LINKPATH $(conditions)/<windows-api>store/$(.cpu-arch-amd64) : [ path.native [ path.join $(storeLibPath) "amd64" ] ] ;
            toolset.flags msvc.link LINKPATH $(conditions)/<windows-api>store/$(.cpu-arch-arm) : [ path.native [ path.join $(storeLibPath) "arm" ] ] ;
        }

        # LTO
        toolset.flags msvc.compile OPTIONS $(conditions)/<lto>on : /GL ;
        toolset.flags msvc.link LINKFLAGS $(conditions)/<lto>on : /LTCG ;

        # Set version-specific flags.
        configure-version-specific msvc : $(version) : $(conditions) ;
    }
}


# Returns the default installation path for the given version.
#
local rule default-path ( version )
{
    local result ;
    {
        # try to use vswhere
        local pseudo_env_VSCOMNTOOLS ;
        local all-env-paths ;
        local root = [ os.environ "ProgramFiles(x86)" ] ;
        if ( ! $(root) )
        {
            root = [ os.environ "ProgramFiles" ] ;
        }
        if ( ! $(root) ) && [ os.name ] in CYGWIN
        {
            # We probably are in an 'env -i' Cygwin session, where the user
            # was unable restore the "ProgramFiles(x86)" environment variable,
            # because it is an invalid environment variable name in Cygwin.
            # However, we can try to query cygpath instead.
            root = [ SHELL "cygpath -w -F 42" : strip-eol ] ; # CSIDL_PROGRAM_FILESX86
            if ( ! $(root) )
            {
                root = [ SHELL "cygpath -w -F 38" : strip-eol ] ; # CSIDL_PROGRAM_FILES
            }
        }
        # When we are a Cygwin build, [ SHELL ] does execute using "/bin/sh -c".
        # When /bin/sh does find a forward slash, no PATH search is performed,
        # causing [ SHELL "C:\\...\\Installer/vswhere.exe" ] to succeed.
        # And fortunately, forward slashes do also work in native Windows.
        local vswhere = "$(root)/Microsoft Visual Studio/Installer/vswhere.exe" ;
        # The check for $(root) is to avoid a segmentation fault if not found.
        if $(version) in 14.1 14.2 14.3 default && $(root) && [ path.exists $(vswhere) ]
        {
            local req = "-requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64" ;
            local prop = "-property installationPath" ;
            local limit ;

            if $(version) = 14.3
            {
                limit = "-version \"[17.0,18.0)\" -prerelease" ;
            }
            else if $(version) = 14.2 || $(version) = "default"
            {
                limit = "-version \"[16.0,17.0)\"" ;
            }
            else if $(version) = 14.1
            {
                limit = "-version \"[15.0,16.0)\"" ;
            }

            # Quoting the "*" is for when we are a Cygwin build, to bypass /bin/sh.
            local vswhere_cmd = "\"$(vswhere)\" -latest -products \"*\" $(req) $(prop) $(limit)" ;
            # The split character "\r" is for when we are a Cygwin build.
            local shell_ret = [ SPLIT_BY_CHARACTERS [ SHELL $(vswhere_cmd) ] : "\r\n" ] ;
            pseudo_env_VSCOMNTOOLS = [ path.native [ path.join $(shell_ret) "\\Common7\\Tools" ] ] ;
            if ! [ path.exists $(pseudo_env_VSCOMNTOOLS) ]
            {
                return ; # Not found.  If we have vswhere, assume that it works.
            }
            all-env-paths = $(pseudo_env_VSCOMNTOOLS) ;
        }
        else
        {
            all-env-paths = [ sequence.transform os.environ
                : $(.version-$(version)-env) ] ;
        }

        # Check environment or previous path_VS150
        for local env-path in $(all-env-paths)
        {
            if $(env-path) && $(.version-$(version)-path)
            {
                for local bin-path in $(.version-$(version)-path)
                {
                    result = [ path.glob [ path.make $(env-path) ] : $(bin-path) ] ;
                    if $(result)
                    {
                        result = [ path.native $(result[1]) ] ;
                        break ;
                    }
                }
            }
            if $(result)
            {
                break ;
            }
        }
    }

    return $(result) ;
}



rule get-rspline ( target : lang-opt lang-flags )
{
    CC_RSPLINE on $(target) = [ on $(target) return $(lang-opt) -U$(UNDEFS)
        $($(lang-flags)) $(OPTIONS) -D$(DEFINES)
        \"-I$(INCLUDES:W)\" \"-FI$(FORCE_INCLUDES:W)\" ] ;
}

class msvc-linking-generator : linking-generator
{
    # Calls the base version.  If necessary, also create a target for the
    # manifest file.specifying source's name as the name of the created
    # target. As result, the PCH will be named whatever.hpp.gch, and not
    # whatever.gch.
    rule generated-targets ( sources + : property-set : project name ? )
    {
        local result = [ linking-generator.generated-targets $(sources)
          : $(property-set) : $(project) $(name) ] ;

        if $(result)
        {
            local name-main = [ $(result[1]).name ] ;
            local action = [ $(result[1]).action ] ;

            if [ $(property-set).get <debug-symbols> ] = "on"
            {
                # We force the exact name on PDB. The reason is tagging -- the
                # tag rule may reasonably special case some target types, like
                # SHARED_LIB. The tag rule will not catch PDBs, and it cannot
                # even easily figure out if a PDB is paired with a SHARED_LIB,
                # EXE or something else. Because PDBs always get the same name
                # as the main target, with .pdb as extension, just force it.
                local target = [ class.new file-target $(name-main:S=.pdb) exact
                    : PDB : $(project) : $(action) ] ;
                local registered-target = [ virtual-target.register $(target) ]
                    ;
                if $(target) != $(registered-target)
                {
                    $(action).replace-targets $(target) : $(registered-target) ;
                }
                result += $(registered-target) ;
            }

            if [ $(property-set).get <embed-manifest> ] = "off"
            {
                # Manifest is an evil target. It has .manifest appened to the
                # name of the main target, including extension, e.g.
                # a.exe.manifest. We use the 'exact' name to achieve this
                # effect.
                local target = [ class.new file-target $(name-main).manifest
                    exact : MANIFEST : $(project) : $(action) ] ;
                local registered-target = [ virtual-target.register $(target) ]
                    ;
                if $(target) != $(registered-target)
                {
                    $(action).replace-targets $(target) : $(registered-target) ;
                }
                result += $(registered-target) ;
            }
        }
        return $(result) ;
    }
}


# Unsafe worker rule for the register-toolset() rule. Must not be called
# multiple times.
#
local rule register-toolset-really ( )
{
    feature.extend toolset : msvc ;

    # Intel and msvc supposedly have link-compatible objects.
    feature.subfeature toolset msvc : vendor : intel : propagated optional ;

    # Inherit MIDL flags.
    toolset.inherit-flags msvc : midl ;

    # Inherit MC flags.
    toolset.inherit-flags msvc : mc ;

    # Dynamic runtime comes only in MT flavour.
    toolset.add-requirements
        <toolset>msvc,<runtime-link>shared:<threading>multi ;

    # Declare msvc toolset specific features.
    {
        feature.feature debug-store : object database : propagated ;
        feature.feature pch-source  :                 : dependency free ;
    }

    # Declare generators.
    {
        # TODO: Is it possible to combine these? Make the generators
        # non-composing so that they do not convert each source into a separate
        # .rsp file.
        generators.register [ new msvc-linking-generator msvc.link :
            OBJ SEARCHED_LIB STATIC_LIB IMPORT_LIB : EXE : <toolset>msvc ] ;
        generators.register [ new msvc-linking-generator msvc.link.dll :
            OBJ SEARCHED_LIB STATIC_LIB IMPORT_LIB : SHARED_LIB IMPORT_LIB :
            <toolset>msvc <suppress-import-lib>false ] ;
        generators.register [ new msvc-linking-generator msvc.link.dll :
            OBJ SEARCHED_LIB STATIC_LIB IMPORT_LIB : SHARED_LIB :
            <toolset>msvc <suppress-import-lib>true ] ;

        generators.register-archiver msvc.archive : OBJ : STATIC_LIB : <toolset>msvc ;
        generators.register-c-compiler msvc.compile.c++ : CPP : OBJ : <toolset>msvc ;
        generators.register-c-compiler msvc.compile.c : C : OBJ : <toolset>msvc ;
        generators.register-c-compiler msvc.compile.c++.preprocess : CPP : PREPROCESSED_CPP : <toolset>msvc ;
        generators.register-c-compiler msvc.compile.c.preprocess : C : PREPROCESSED_C : <toolset>msvc ;

        # Using 'register-c-compiler' adds the build directory to INCLUDES.
        generators.register-c-compiler msvc.compile.rc : RC : OBJ(%_res) : <toolset>msvc ;
        generators.override msvc.compile.rc : rc.compile.resource ;
        generators.register-standard msvc.compile.asm : ASM : OBJ : <toolset>msvc ;

        generators.register-c-compiler msvc.compile.idl : IDL : MSTYPELIB H C(%_i) C(%_proxy) C(%_dlldata) : <toolset>msvc ;
        generators.override msvc.compile.idl : midl.compile.idl ;

        generators.register-standard msvc.compile.mc : MC : H RC : <toolset>msvc ;
        generators.override msvc.compile.mc : mc.compile ;

        # Note: the 'H' source type will catch both '.h' and '.hpp' headers as
        # the latter have their HPP type derived from H. The type of compilation
        # is determined entirely by the destination type.
        generators.register [ new msvc-pch-generator msvc.compile.c.pch   : H :   C_PCH OBJ : <pch>on <toolset>msvc ] ;
        generators.register [ new msvc-pch-generator msvc.compile.c++.pch : H : CPP_PCH OBJ : <pch>on <toolset>msvc ] ;

        generators.override msvc.compile.c.pch   : pch.default-c-pch-generator ;
        generators.override msvc.compile.c++.pch : pch.default-cpp-pch-generator ;
    }

    toolset.flags msvc.compile PCH_FILE   <pch>on : <pch-file>   ;
    toolset.flags msvc.compile PCH_SOURCE <pch>on : <pch-source> ;
    toolset.flags msvc.compile PCH_HEADER <pch>on : <pch-header> ;

    #
    # Declare flags for compilation.
    #

    toolset.flags msvc.compile OPTIONS <optimization>speed : /O2 ;
    toolset.flags msvc.compile OPTIONS <optimization>space : /O1 ;

    toolset.flags msvc.compile OPTIONS $(.cpu-arch-ia64)/<instruction-set>$(.cpu-type-itanium) : /G1 ;
    toolset.flags msvc.compile OPTIONS $(.cpu-arch-ia64)/<instruction-set>$(.cpu-type-itanium2) : /G2 ;

    toolset.flags msvc.compile OPTIONS <debug-symbols>on/<debug-store>object : /Z7 ;
    toolset.flags msvc.compile OPTIONS <debug-symbols>on/<debug-store>database : /Zi ;
    toolset.flags msvc.compile OPTIONS <optimization>off : /Od ;
    toolset.flags msvc.compile OPTIONS <inlining>off : /Ob0 ;
    toolset.flags msvc.compile OPTIONS <inlining>on : /Ob1 ;
    toolset.flags msvc.compile OPTIONS <inlining>full : /Ob2 ;

    toolset.flags msvc.compile OPTIONS <warnings>on : /W3 ;
    toolset.flags msvc.compile OPTIONS <warnings>off : /W0 ;
    toolset.flags msvc.compile OPTIONS <warnings>all : /W4 ;
    toolset.flags msvc.compile OPTIONS <warnings>extra : /W4 ;
    toolset.flags msvc.compile OPTIONS <warnings>pedantic : /W4 ;
    toolset.flags msvc.compile OPTIONS <warnings-as-errors>on : /WX ;

    toolset.flags msvc.compile C++FLAGS  <exception-handling>on/<asynch-exceptions>off/<extern-c-nothrow>off : /EHs ;
    toolset.flags msvc.compile C++FLAGS  <exception-handling>on/<asynch-exceptions>off/<extern-c-nothrow>on : /EHsc ;
    toolset.flags msvc.compile C++FLAGS  <exception-handling>on/<asynch-exceptions>on/<extern-c-nothrow>off : /EHa ;
    toolset.flags msvc.compile C++FLAGS  <exception-handling>on/<asynch-exceptions>on/<extern-c-nothrow>on : /EHac ;

    toolset.flags msvc.compile C++FLAGS  <cxxstd>14 : "/std:c++14" ;
    toolset.flags msvc.compile C++FLAGS  <cxxstd>17 : "/std:c++17" ;
    toolset.flags msvc.compile C++FLAGS  <cxxstd>20 : "/std:c++20" ;
    toolset.flags msvc.compile C++FLAGS  <cxxstd>latest : "/std:c++latest" ;

    # By default 8.0 enables rtti support while prior versions disabled it. We
    # simply enable or disable it explicitly so we do not have to depend on this
    # default behaviour.
    toolset.flags msvc.compile C++FLAGS <rtti>on : /GR ;
    toolset.flags msvc.compile C++FLAGS <rtti>off : /GR- ;
    toolset.flags msvc.compile OPTIONS <runtime-debugging>off/<runtime-link>shared : /MD ;
    toolset.flags msvc.compile OPTIONS <runtime-debugging>on/<runtime-link>shared : /MDd ;

    toolset.flags msvc.compile OPTIONS <runtime-debugging>off/<runtime-link>static/<threading>multi : /MT ;
    toolset.flags msvc.compile OPTIONS <runtime-debugging>on/<runtime-link>static/<threading>multi : /MTd ;

    toolset.flags msvc.compile CFLAGS <cflags> : ;
    toolset.flags msvc.compile.c++ C++FLAGS <cxxflags> : ;

    toolset.flags msvc.compile PDB_CFLAG <debug-symbols>on/<debug-store>database : /Fd ;

    toolset.flags msvc.compile DEFINES <define> ;
    toolset.flags msvc.compile UNDEFS <undef> ;
    toolset.flags msvc.compile INCLUDES <include> ;
    toolset.flags msvc.compile FORCE_INCLUDES <force-include> ;

    # Declare flags for the assembler.
    toolset.flags msvc.compile.asm USER_ASMFLAGS <asmflags> ;

    toolset.flags msvc.compile.asm ASMFLAGS <architecture>x86/<debug-symbols>on : "/Zi /Zd" ;

    toolset.flags msvc.compile.asm ASMFLAGS <architecture>x86/<warnings>on : /W3 ;
    toolset.flags msvc.compile.asm ASMFLAGS <architecture>x86/<warnings>off : /W0 ;
    toolset.flags msvc.compile.asm ASMFLAGS <architecture>x86/<warnings>all : /W4 ;
    toolset.flags msvc.compile.asm ASMFLAGS <architecture>x86/<warnings-as-errors>on : /WX ;

    toolset.flags msvc.compile.asm ASMDEFINES <architecture>x86 : <define> ;

    # Declare flags for linking.
    {
        toolset.flags msvc.link PDB_LINKFLAG <debug-symbols>on/<debug-store>database : "/PDB:" ;  # not used yet
        toolset.flags msvc.link LINKFLAGS <debug-symbols>on : /DEBUG ;
        toolset.flags msvc.link DEF_FILE <def-file> ;
        toolset.flags msvc.link MANIFEST_FILE <embed-manifest-via>linker : <embed-manifest-file> ;

        # The linker disables the default optimizations when using /DEBUG so we
        # have to enable them manually for release builds with debug symbols.
        toolset.flags msvc LINKFLAGS <debug-symbols>on/<runtime-debugging>off : "/OPT:REF,ICF" ;

        toolset.flags msvc LINKFLAGS <user-interface>console : "/subsystem:console" ;
        toolset.flags msvc LINKFLAGS <user-interface>gui : "/subsystem:windows" ;
        toolset.flags msvc LINKFLAGS <user-interface>wince : "/subsystem:windowsce" ;
        toolset.flags msvc LINKFLAGS <user-interface>native : "/subsystem:native" ;
        toolset.flags msvc LINKFLAGS <user-interface>auto : "/subsystem:posix" ;

        toolset.flags msvc.link LINKFLAGS <linkflags> ;
        toolset.flags msvc.link LINKPATH <library-path> ;

        toolset.flags msvc.link FINDLIBS_ST <find-static-library> ;
        toolset.flags msvc.link FINDLIBS_SA <find-shared-library> ;
        toolset.flags msvc.link LIBRARY_OPTION <toolset>msvc : "" : unchecked ;
        toolset.flags msvc.link LIBRARIES_MENTIONED_BY_FILE : <library-file> ;
    }

    toolset.flags msvc.archive AROPTIONS <archiveflags> ;

    # Enable response file control
    toolset.flags msvc RESPONSE_FILE_SUB <response-file>auto : a ;
    toolset.flags msvc RESPONSE_FILE_SUB <response-file>file : f ;
    toolset.flags msvc RESPONSE_FILE_SUB <response-file>contents : c ;

    # Create a project to allow building the setup scripts
    project.initialize $(__name__) ;
    .project = [ project.current ] ;
    project msvc ;

    feature.feature msvc.setup-options : : free ;
}


# Locates the requested setup script under the given folder and returns its full
# path or nothing in case the script can not be found. In case multiple scripts
# are found only the first one is returned.
#
# TODO: There used to exist a code comment for the msvc.init rule stating that
# we do not correctly detect the location of the vcvars32.bat setup script for
# the free VC7.1 tools in case user explicitly provides a path. This should be
# tested or simply remove this whole comment in case this toolset version is no
# longer important.
#
local rule locate-default-setup ( command : parent : setup-name )
{
    local result = [ GLOB $(command) $(parent) : $(setup-name) ] ;
    if $(result[1])
    {
        return $(result[1]) ;
    }
}


# Validates given path, registers found configuration and prints debug
# information about it.
#
local rule register-configuration ( version : path ? )
{
    if $(path)
    {
        local command = [ GLOB $(path) : cl.exe ] ;

        if $(command)
        {
            if $(.debug-configuration)
            {
                ECHO notice\: "[msvc-cfg]" msvc-$(version) detected, command\:
                    '$(command)' ;
            }

            $(.versions).register $(version) ;
            $(.versions).set $(version) : default-command : $(command) ;
        }
    }
}


################################################################################
#
#   Startup code executed when loading this module.
#
################################################################################

if [ MATCH (--debug-configuration) : [ modules.peek : ARGV ] ]
{
    .debug-configuration = true ;
}

# Miscellaneous constants.
.RM = [ common.rm-command ] ;
.nl = "
" ;
_ = " " ;
.ProgramFiles = [ path.make [ common.get-program-files-dir ] ] ;
.escaped-double-quote = "\"" ;
.hash = "\#" ;
.TOUCH_FILE = [ common.file-touch-command ] ;

# List of all registered configurations.
.versions = [ new configurations ] ;

# Supported CPU architectures.
.cpu-arch-info-i386  = x86  32 ;
.cpu-arch-info-amd64 = x86  64 ;
.cpu-arch-info-ia64  = ia64 64 ;
.cpu-arch-info-arm   = arm  32 ;
.cpu-arch-info-arm64 = arm  64 ;

# Fill explicit architecture and address model values
for local cpu in [ MATCH "^\\.cpu-arch-info-(.*)" : [ VARNAMES $(__name__) ] ]
{
    local arch = $(.cpu-arch-info-$(cpu)[1]) ;
    .cpus-on-$(arch) += $(cpu) ;
    .cpu-arch-$(cpu) = <architecture>$(arch)/<address-model>$(.cpu-arch-info-$(cpu)[2]) ;
}

# Match implicit architecture and address model based on the current platform
.default-cpu-arch = [ os.environ PROCESSOR_ARCHITEW6432 ] ;
.default-cpu-arch ?= [ os.environ PROCESSOR_ARCHITECTURE ] ;
.default-cpu-arch = $(.default-cpu-arch:L) ;
switch $(.default-cpu-arch)
{
    case x86   : .default-cpu-arch = i386 ;
    case em64t : .default-cpu-arch = amd64 ;
}

for local cpu in $(.cpus-on-$(.cpu-arch-info-$(.default-cpu-arch)[1]))
{
    .cpu-arch-$(cpu) += <architecture>/<address-model>$(.cpu-arch-info-$(cpu)[2]) ;
}

.cpu-arch-$(.default-cpu-arch) += <architecture>$(.cpu-arch-info-$(.default-cpu-arch)[1])/<address-model> ;
.cpu-arch-$(.default-cpu-arch) += <architecture>/<address-model> ;

# If there is only one address model for an architecture we allow to ommit it
for local arch in [ MATCH "^\\.cpus-on-(.*)" : [ VARNAMES $(__name__) ] ]
{
    if ! $(.cpus-on-$(arch)[2-]) && $(.cpus-on-$(arch)[1]) != $(.default-cpu-arch)
    {
        .cpu-arch-$(.cpus-on-$(arch)) += <architecture>$(arch)/<address-model> ;
    }
}


# Supported CPU types (only Itanium optimization options are supported from
# VC++ 2005 on). See
# http://msdn2.microsoft.com/en-us/library/h66s5s0e(vs.90).aspx for more
# detailed information.
.cpu-type-g5       = i586 pentium pentium-mmx ;
.cpu-type-g6       = i686 pentiumpro pentium2 pentium3 pentium3m pentium-m k6
                     k6-2 k6-3 winchip-c6 winchip2 c3 c3-2 c7 ;
.cpu-type-em64t    = prescott nocona core2 corei7 corei7-avx core-avx-i
                     conroe conroe-xe conroe-l allendale merom
                     merom-xe kentsfield kentsfield-xe penryn wolfdale
                     yorksfield nehalem sandy-bridge ivy-bridge haswell
                     broadwell skylake skylake-avx512 cannonlake icelake-client
                     icelake-server cascadelake cooperlake tigerlake ;
.cpu-type-amd64    = k8 opteron athlon64 athlon-fx k8-sse3 opteron-sse3
                     athlon64-sse3 amdfam10 barcelona bdver1 bdver2 bdver3
                     bdver4 btver1 btver2 znver1 znver2 ;
.cpu-type-g7       = pentium4 pentium4m athlon athlon-tbird athlon-4 athlon-xp
                     athlon-mp $(.cpu-type-em64t) $(.cpu-type-amd64) ;
.cpu-type-itanium  = itanium itanium1 merced ;
.cpu-type-itanium2 = itanium2 mckinley ;
.cpu-type-arm      = armv2 armv2a armv3 armv3m armv4 armv4t armv5 armv5t armv5te armv6 armv6j iwmmxt ep9312
                     armv7 armv7s ;

# Known toolset versions, in order of preference.
.known-versions = 14.3 14.2 14.1 14.0 12.0 11.0 10.0 10.0express 9.0 9.0express 8.0 8.0express 7.1
    7.1toolkit 7.0 6.0 ;

# Version aliases.
.version-alias-6 = 6.0 ;
.version-alias-6.5 = 6.0 ;
.version-alias-7 = 7.0 ;
.version-alias-8 = 8.0 ;
.version-alias-9 = 9.0 ;
.version-alias-10 = 10.0 ;
.version-alias-11 = 11.0 ;
.version-alias-12 = 12.0 ;
.version-alias-14 = 14.0 ;

# Names of registry keys containing the Visual C++ installation path (relative
# to "HKEY_LOCAL_MACHINE\SOFTWARE\\Microsoft").
.version-6.0-reg = "VisualStudio\\6.0\\Setup\\Microsoft Visual C++" ;
.version-7.0-reg = "VisualStudio\\7.0\\Setup\\VC" ;
.version-7.1-reg = "VisualStudio\\7.1\\Setup\\VC" ;
.version-8.0-reg = "VisualStudio\\8.0\\Setup\\VC" ;
.version-8.0express-reg = "VCExpress\\8.0\\Setup\\VC" ;
.version-9.0-reg = "VisualStudio\\9.0\\Setup\\VC" ;
.version-9.0express-reg = "VCExpress\\9.0\\Setup\\VC" ;
.version-10.0-reg = "VisualStudio\\10.0\\Setup\\VC" ;
.version-10.0express-reg = "VCExpress\\10.0\\Setup\\VC" ;
.version-11.0-reg = "VisualStudio\\11.0\\Setup\\VC" ;
.version-12.0-reg = "VisualStudio\\12.0\\Setup\\VC" ;
.version-14.0-reg = "VisualStudio\\14.0\\Setup\\VC" ;

# Visual C++ Toolkit 2003 does not store its installation path in the registry.
# The environment variable 'VCToolkitInstallDir' and the default installation
# path will be checked instead.
.version-7.1toolkit-path    = "Microsoft Visual C++ Toolkit 2003/bin" ;
.version-7.1toolkit-env     = VCToolkitInstallDir ;
# Visual Studio 2017 doesn't use a registry at all. And the suggested methods
# of discovery involve having a compiled program. So as a fallback we search
# paths for VS2017 (aka msvc >= 14.1).
.version-14.1-path =
    "../../VC/Tools/MSVC/*/bin/Host*/*"
    "Microsoft Visual Studio/2017/*/VC/Tools/MSVC/*/bin/Host*/*"
    ;
.version-14.1-env = VS150COMNTOOLS ProgramFiles ProgramFiles(x86) ;
.version-14.2-path =
    "../../VC/Tools/MSVC/*/bin/Host*/*"
    "Microsoft Visual Studio/2019/*/VC/Tools/MSVC/*/bin/Host*/*"
    ;
.version-14.2-env = VS160COMNTOOLS ProgramFiles ProgramFiles(x86) ;
.version-14.3-path =
    "../../VC/Tools/MSVC/*/bin/Host*/*"
    "Microsoft Visual Studio/2022/*/VC/Tools/MSVC/*/bin/Host*/*"
    ;
.version-14.3-env = VS170COMNTOOLS ProgramFiles ProgramFiles(x86) ;

# Auto-detect all the available msvc installations on the system.
auto-detect-toolset-versions ;


# And finally trigger the actual Boost Build toolset registration.
register-toolset ;
